package com.godenwater.recv.server.all;

import com.godenwater.recv.decode.*;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.godenwater.recv.model.CommonMessage;

import cn.gov.mwr.sl651.Symbol;
import cn.gov.mwr.sl651.utils.ByteUtil;

import java.net.InetSocketAddress;

/**
 * 报文解码类，作为上行报文的解码类，此类必须实现对所有消息体的封闭细节
 *
 * @author lipujun
 * @ClassName: YfServerDataDecoder
 * @Description: 处理断包和粘包的实现类
 * @date Mar 2, 2013
 */
public class ServerDataDecoder extends CumulativeProtocolDecoder {
    private static final AttributeKey BUF_BYTE = new AttributeKey(
            ServerDataDecoder.class, "BUF_KEY");

    private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private String channel;

    public ServerDataDecoder(String channel) {
        this.channel = channel;
    }

    /**
     * 这个办法的返回值是重点：
     * <p>
     * 1、当内容正好时，返回false，告诉父类可以进行下一批消息的处理
     * <p>
     * 2、内容不符时，需要下一批消息的内容，此时返回false
     * ，如许父类会将内容放进IoSession中，等下次数据来后就主动拼装再交给本类的doDecode
     * <p>
     * 3、当内容多时，返回true，因为需要再将本批数据进行读取，父类会将残剩的数据再次推送本类的doDecode
     * <p>
     * 简而言之，当你认为读取到的数据已经够解码了，那么就返回true，否则就返回false。
     */
    @Override
    protected boolean doDecode(IoSession session, IoBuffer buffer,
                               ProtocolDecoderOutput out) throws Exception {
        String ip = "";
        Integer port = 0;
        try {
            ip = ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress();
            port = ((InetSocketAddress) session.getRemoteAddress()).getPort();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 将此报文日志，记录到RTU单独的文件中，此类报文内容有可能会出现粘包的报文
        //logger.info("R> " + this.channel + " " + StringUtils.trimAllWhitespace(buffer.getHexDump()));

        // logger.info(">> HEX : " +  ByteUtil.toHexString(buffer.buf().array()));

        CommonMessage message;

        if (buffer.remaining() > 0) {// 表示缓冲区中有数据
            // System.out.println("------buffer.remaining() ---------"
            // + buffer.remaining() );
            int size = 0;

            boolean wxFlag = false;// 卫星发送标识位

            buffer.mark();// 标记当前位置，以便reset

            //读取第一个字节用于协议判断标识
            byte[] mode = new byte[1];
            buffer.get(mode);

            // ---------------------------------------------------------
            /**
             *表示是否为卫星的数据，或者为亿立能的数据
             * 1、卫星前缀：24545853432C642C3133343431352C37382C
             * 2、亿立能前缀：24414141
             * 亿立能的数据前缀为ASCII为$AAA或$BAA,$的16进制数据为24
             */
            if (mode[0] == (byte) 0x24) {

                byte[] ylnBit = new byte[3];//添加对亿立能数据的判断,A 的十六进制为41
                buffer.get(ylnBit);
                if (ylnBit[0] == (byte) 0x41 || ylnBit[0] == (byte) 0x42) {
                    // 表示为亿立能数据，需接收到后续的所有字节数据进行处理
                    message = YlnMessageDecoder.decode(ylnBit, buffer);
                    out.write(message);
                    return true;
                } else {

                    // 共计18个字节，已读取1字节，需再跳过17个字节
                    byte[] BD = new byte[17];
                    buffer.get(BD);

                    wxFlag = true;
                    // 重新获取一个字节
                    buffer.get(mode);
                }
            }

            //--------------------------------------------------------------
            // 如果是宏电模块发送过来的数据，则需要通过此模块进行解析
            if (mode[0] == (byte) 0x7B) {
                // 宏电模块，需接收到后续的所有字节数据
                // 1、表示是宏电的协议
                message = HdMessageDecoder.decode(channel, buffer);
                out.write(message);
                return true;
            }
            /**
             //燕禹心跳包，此包为加载宏电模块后的通过UDP协议传输上来的数据包
             if (mode[0] == (byte) 0x7B) {
             // 7B01001631333931323334353637380AC8C78F0FA27B
             // 需要跳过22个字节
             byte[] BD = new byte[buffer.remaining()];
             buffer.get(BD);
             return true;
             }
             */

            /**
             * ---------------------------------------------------------
             * 表示是通过GSM传输过来的数据，此处有可能会出现问题
             *43484C5B305D2D3E6765742061206E657720736D73200D0A
             * 共计24个字节
             */
            if (mode[0] == (byte) 0x43) {
                // 共24个字节，已读取1字节，需再跳过23个字节
                byte[] gsmbytes = new byte[23];
                buffer.get(gsmbytes);

                wxFlag = true;
                // 重新获取一个字节，为后续的正确报文读取提供规则
                buffer.get(mode);
            }


            /**
             *---------------------------------------------------------
             * 表示是水文协议
             */
            if (mode[0] == (byte) Symbol.SOH_ASC) {
                // 1、表示是水文的ASC码协议
                message = Sl651MessageDecoder.decodeAsc(buffer);
            } else if (mode[0] == (byte) Symbol.SOH_HEX) {
                // 2、表示是水文的HEX码协议
                byte[] secondBit = new byte[1];
                buffer.get(secondBit);

                // 表示是7E7E，水文的16进制报文数据
                if (secondBit[0] == (byte) Symbol.SOH_HEX) {
                    message = Sl651MessageDecoder.decodeHex(buffer);
                } else {
                    // 不能使用7E做为中心站名

                    byte[] threedBit = new byte[1];
                    buffer.get(threedBit);
                    //屏蔽异常报文7e12d20ec7ffffffffffffffffffffffffffffffffffffffffc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1710d2
                    if (secondBit[0] == (byte) 0x12 && threedBit[0] == (byte) 0xd2) {
//                        logger.info(">> 非协议报文ip:" + ip + " 端口:" + port);
                        byte[] remain = new byte[buffer.remaining()];
                        buffer.get(remain);
                        logger.info(">> 非协议报文2 ip:" + ip + " 端口：" + port + " 报文内容：" + ByteUtil.toHexString(mode[0]) + ByteUtil.byteToHexString(remain));
                        return true;
                    }
                    message = YanyuMessageDecoder.decode(buffer, secondBit, threedBit);

                }

            } else if (mode[0] == (byte) 0x68) {
                // 水资源协议
                message = Szy206MessageDecoder.decode(buffer);

            } else {
                // 处理DTU设备上线时的登录信息，发的是DTU编号和手机号
                // buffer.get(buffer.array().length);
                byte[] remain = new byte[buffer.remaining()];
                buffer.get(remain);
                logger.info(">> 非协议报文2 ip:" + ip + " 端口：" + port + " 报文内容：" + ByteUtil.toHexString(mode[0]) + ByteUtil.byteToHexString(remain));
                return true;
            }

            size = message.getBodySize() + 1 + message.getCrclen();

            // 若是消息内容的长度不敷则直接返回true
            //System.out.println(">> size " + size +  " crclen "+ message.getCrclen()+ " remain " + buffer.remaining());

            if (size > buffer.remaining()) {// 若是消息大小与缓冲区中的内容大小不匹配，则重置，相当于不读取size
                // System.out.println("------size > buffer.remaining() ---------"
                // + size + "  " + buffer.remaining());
                buffer.reset();
                return false;// 接管新数据，以拼凑成完全数据
            } else {
                // System.out
                // .println("------size < buffer.remaining() ---------BodySizeLen "
                // + BodySizeLen
                // + " size "
                // + size
                // + "  "
                // + buffer.remaining());
                byte[] bodyContent = new byte[message.getBodySize()];// 根据前面header的内容来获取长度
                buffer.get(bodyContent);

                // 终止符
                byte[] eof = new byte[1];
                // 校验码
                byte[] crc = new byte[message.getCrclen()];

                // 两种协议的CRC校验符与结束符，正好相反
                if (mode[0] == (byte) 0x68) {
                    // 水资源协议
                    buffer.get(crc);
                    buffer.get(eof);
                } else {
                    // 水文协议
                    buffer.get(eof);
                    buffer.get(crc);
                }

                // 组装消息
                // message.setHeader(message.getHeader());
                message.setContents(bodyContent);
                message.setEOF(eof[0]);
                message.setCRC(crc);

                out.write(message);

                if (wxFlag) {
                    // buffer.reset();
                    // buffer.clear();
                    // 提取后续的字节，不做处理，直接当垃圾
                    byte[] remain = new byte[buffer.remaining()];
                    buffer.get(remain);

                    // logger.info("+++++ 【" + remain.length +
                    // buffer.remaining());
                    return false;
                }

                if (buffer.remaining() > 0) {// 若是读取内容后还粘了包，就让父类再给解析一次，返回true进行下一次解析
                    buffer.mark();// 重新标记当前位置，以便reset
                    return true;
                }
            }
        }
        return false;// 处理重发成功，让父类进行接管下个包
    }

    private int parseLength(byte[] bodyLen) {
        byte A1 = (byte) (bodyLen[0] & 0x0f);
        byte A2 = bodyLen[1];
        return ByteUtil.bytesToUshort(new byte[]{A1, A2});
    }

    private CommonMessage decodeSl651Asc(IoBuffer buffer) {

        cn.gov.mwr.sl651.header.AscHeader header = new cn.gov.mwr.sl651.header.AscHeader();
        header.setStartBit(new byte[]{Symbol.SOH_ASC});

        // 上行的，中心站地址在前
        byte[] centerAddr = new byte[header.getCenterAddrLen()];
        buffer.get(centerAddr);
        header.setCenterAddr(centerAddr);

        // 上行的，遥测站地址在前
        byte[] stationAddr = new byte[header.getStationAddrLen()];
        buffer.get(stationAddr);
        header.setStationAddr(stationAddr);

        byte[] password = new byte[header.getPasswordLen()];
        buffer.get(password);
        header.setPassword(password);

        byte[] funcCode = new byte[header.getFuncCodeLen()];
        buffer.get(funcCode);
        header.setFuncCode(funcCode);

        byte[] BodySize = new byte[header.getBodySizeLen()];
        buffer.get(BodySize);
        String hexBodySizeStr = new String(BodySize);
        byte[] hexBodySize = ByteUtil.HexStringToBinary(hexBodySizeStr);
        header.setBodySize(BodySize);

        byte[] bodyStartBit = new byte[header.getBodyStartBitLen()];
        buffer.get(bodyStartBit);
        header.setBodyStartBit(bodyStartBit);

        if (header.getBodyStartBit()[0] == Symbol.SYN) {
            byte[] bodyCount = new byte[header.getBodyCountLen()];
            buffer.get(bodyCount);
            header.setBodyCount(bodyCount);
        }

        // 转换报文中的上下行标识及报文长度
        int bodySizeLen = parseLength(hexBodySize);// 需要考虑拆分字节;
        if (bodyStartBit[0] == Symbol.SYN) {
            bodySizeLen = bodySizeLen - header.getBodyCountLen();
        }
        int crclen = 4;

        // --------------------------------
        CommonMessage message = new CommonMessage();
        message.setHeader(header);
        message.setCrclen(crclen);
        message.setBodySize(bodySizeLen);

        return message;
    }

    private CommonMessage decodeSl651Hex(IoBuffer buffer) {

        cn.gov.mwr.sl651.header.HexHeader header = new cn.gov.mwr.sl651.header.HexHeader();

        header.setStartBit(new byte[]{Symbol.SOH_HEX, Symbol.SOH_HEX});

        // 上行的，中心站地址在前
        byte[] centerAddr = new byte[header.getCenterAddrLen()];
        buffer.get(centerAddr);
        header.setCenterAddr(centerAddr);

        // 上行的，遥测站地址在后
        byte[] stationAddr = new byte[header.getStationAddrLen()];
        buffer.get(stationAddr);
        header.setStationAddr(stationAddr);

        // 解析密码
        byte[] password = new byte[header.getPasswordLen()];
        buffer.get(password);
        header.setPassword(password);

        // 解析 功能码
        byte[] funcCode = new byte[header.getFuncCodeLen()];
        buffer.get(funcCode);
        header.setFuncCode(funcCode);

        // 解析报文上行标识及消息体长度
        byte[] BodySize = new byte[header.getBodySizeLen()];
        buffer.get(BodySize);
        header.setBodySize(BodySize);

        // 解析消息开始体
        byte[] bodyStartBit = new byte[header.getBodyStartBitLen()];
        buffer.get(bodyStartBit);
        header.setBodyStartBit(bodyStartBit);

        // 如果是SYN，表示是多包发送。多包发送，一次确认的传输模式中使用
        if (bodyStartBit[0] == Symbol.SYN) {
            byte[] bodyCount = new byte[header.getBodyCountLen()];
            buffer.get(bodyCount);
            header.setBodyCount(bodyCount);
        }

        // 转换报文中的上下行标识及报文长度
        int bodySizeLen = parseLength(BodySize);// 需要考虑拆分字节;
        if (bodyStartBit[0] == Symbol.SYN) {
            bodySizeLen = bodySizeLen - header.getBodyCountLen();
        }
        int crclen = 2;

        // --------------------------------
        CommonMessage message = new CommonMessage();
        message.setHeader(header);
        message.setCrclen(crclen);
        message.setBodySize(bodySizeLen);

        return message;
    }

    private CommonMessage decodeSZY206(IoBuffer buffer) {

        cn.gov.mwr.szy206.SzyMessageHeader header = new cn.gov.mwr.szy206.SzyMessageHeader();

        header.setStartBit(new byte[]{cn.gov.mwr.szy206.SzySymbol.STX});
        // 报文长度
        byte[] bodySize = new byte[header.getBodySizeLen()];
        buffer.get(bodySize);
        header.setBodySize(bodySize);

        // 报文开始符
        byte[] bodyStartBit = new byte[header.getBodyStartBitLen()];
        buffer.get(bodyStartBit);
        header.setBodyStartBit(bodyStartBit);

        // 转换报文中的上下行标识及报文长度
        int bodySizeLen = ByteUtil.bytesToUbyte(bodySize);// 报文长度计算

        int crclen = 1;

        // --------------------------------
        CommonMessage message = new CommonMessage();
        message.setHeader(header);
        message.setCrclen(crclen);
        message.setBodySize(bodySizeLen);// 需要减去三个字节，一位结束符，两位CRC校验符

        return message;
    }

    private CommonMessage decodeYanyu(IoBuffer buffer, byte[] secondBit) {
        // 燕禹协议
        com.godenwater.yanyu.YYMessageHeader header = new com.godenwater.yanyu.YYMessageHeader();

        // 7E 目的地址 源地址 特征 长度 时间1《数据》 ETX CRC16H CRC16L
        header.setStartBit(new byte[]{Symbol.SOH_HEX});
        // 上行的，中心站地址在前
        header.setCenterAddr(secondBit);

        // 源地址,测站地址
        byte[] stationAddr = new byte[header.getStationAddrLen()];
        buffer.get(stationAddr);
        header.setStationAddr(stationAddr);

        // 特征码
        byte[] funccode = new byte[header.getFuncCodeLen()];
        buffer.get(funccode);
        header.setFuncCode(funccode);

        // 报文长度
        byte[] bodySize = new byte[header.getBodySizeLen()];
        buffer.get(bodySize);
        header.setBodySize(bodySize);

        // 转换报文中的上下行标识及报文长度
        int bodySizeLen = ByteUtil.bytesToUbyte(bodySize);// 需要考虑拆分字节;

        int crclen = 2;

        // --------------------------------
        CommonMessage temp = new CommonMessage();
        temp.setHeader(header);
        temp.setCrclen(crclen);
        temp.setBodySize(bodySizeLen - 3);// 需要减去三个字节，一位结束符，两位CRC校验符

        return temp;
    }

    public void dispose(IoSession session) throws Exception {
        // TODO Auto-generated method stub

    }

    public void finishDecode(IoSession session, ProtocolDecoderOutput out)
            throws Exception {
        // TODO Auto-generated method stub

    }

}
